package scaladuct.hint

import java.lang.reflect.Method
import java.lang.annotation.Annotation
import util.Elvis._
import java.lang.{String, Class}
import collection.immutable.List
import util.AnnotationWrapper
import scaladuct._
import LookupFunctions._

/**
 * <p>
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: Sep 2, 2010
 * Time: 8:10:28 PM
 */

@serializable @SerialVersionUID(1L)
object HintsHelper {
  def extract(method: Method,
              contextCookie: AnyRef,
              starter: Starter[_]): HasHints[_] = {

    var propHints: List[(String, Property[Annotation])] = Nil

    // collect hints from the cookie properties
    val cookieInfo: BeanInfo = BeanUtils.createBeanInfo(contextCookie)
    cookieInfo.asMap.keySet.foreach((propName) => {
      val propAnnots: List[Annotation] = cookieInfo.propertyAnnotations(propName)
      propAnnots.filter(isHint(_)).foreach(hintAnnot => {
        val annotAsProp: Property[Annotation] = PropertyUtils.convertToProperty(AnnotationWrapper(hintAnnot))
        propHints ::= (propName, annotAsProp)
      })
    })

    // collect hints from the phase method
    var methodHints: List[Property[Annotation]] = List()
    method.getAnnotations.filter(annot => isHint(annot)).foreach(annot => {
      methodHints ::= PropertyUtils.convertToProperty(AnnotationWrapper(annot))
    })

    @serializable @SerialVersionUID(1L)
    class AnnotHints[H] extends HasHints[H] {
      type HintType = Annotation

      def hints(subject: String) = if (subject == null)
        methodHints
      else
        propHints.filter(_._1 == subject).map(_._2) // subject == cookie property

      def hintPage(hint: Property[HintType]): ConfigPage[H] = {
        PhaseMethodInterceptor.apply(hintClass(hint), hint.value, starter).page.asInstanceOf[ConfigPage[H]]
      }
    }

    new AnnotHints
  }


  def isHint(value: Any): Boolean = value.isInstanceOf[Hint] ||
          (value.isInstanceOf[Annotation] &&
                  value.asInstanceOf[Annotation].annotationType.getAnnotation(classOf[Hint]) != null)

  def hintClass(value: Any): Class[Any] = {
    require(isHint(value))
    val annot: Annotation = value.asInstanceOf[Annotation]

    val hintAnnotation: Hint = if (annot.isInstanceOf[Hint])
      annot.asInstanceOf[Hint]
    else
      annot.annotationType.getAnnotation(classOf[Hint])

    hintAnnotation.value.asInstanceOf[Class[Any]]
  }

  def hintId(value: Any): String = {
    require(isHint(value))

    val hintAnnotation = value.asInstanceOf[Annotation]

    if (hintAnnotation.isInstanceOf[Hint]) {
      hintAnnotation.asInstanceOf[Hint].id
    } else {
      try {
        hintAnnotation.annotationType.getMethod("id").invoke(hintAnnotation).asInstanceOf[String]
      }
      catch {
        case nsm: NoSuchMethodException => {
          val hintMetaAnnot: Hint = hintAnnotation.annotationType.getAnnotation(classOf[Hint])
          if (hintMetaAnnot != null)
            hintMetaAnnot.id
          else
            null
        }
      }
    }
  }

  def findHint(hints: List[Any], hintCls: Class[_]): Option[Any] =
    hints.find(potentHint => isHint(potentHint) && hintCls == hintClass(potentHint))
}
